// This source code is provided "AS IS"
// PLEASE TAKE BACKUP OF IMPORTANT FILES BEFORE DOING THIS
// AS THIS UTILITY UPDATES ITS FILES

#include <windows.h>
#include <tchar.h>


#include <iostream>


#define ERR_COMMAND_LINE	1
#define ERR_FILE_ACCESS		2
#define ERR_FILE_MEMORY		3
#define ERR_FILE_OPER		4

#define ISSTRC( str, con, count ) !memcmp( str, con, count )
#define ISSTR( str, con ) ISSTRC( str, con, sizeof( con ))

const TCHAR COMMENT_C_START[]	= _T( "/*" );
const TCHAR COMMENT_C_END[]		= _T( "*/" );

const TCHAR COMMENT_CPP_START[]	= _T( "//" );
const TCHAR COMMENT_CPP_END[]	= _T( "\n" );
const TCHAR UNIMPORTANT_TEXT[]	= _T( " \t\r\n" );

const TCHAR QUOTE				= _T( '\"' );
const TCHAR ESCAPE_SEQ			= _T( '\\' );

const TCHAR LINE_FEED			= _T( '\n' );
const TCHAR CARRIAGE_RETURN		= _T( '\r' );

bool g_bCommentLine = false;

void PrintUsage( PTCHAR szAppName );
int ProcessFile( PTCHAR szFileName );
void RemoveComments( PTCHAR szInput, DWORD dwInpCount, PTCHAR szOutput, PDWORD dwOpCount );
void WriteBuffer( PTCHAR szDataString, TCHAR ch, PDWORD dwOpCount, bool flush );
bool HasImportantText( PTCHAR szBuffer, DWORD dwBufLength );


int _tmain( int argc, PTCHAR argv[] )
{
	if( argc < 2 )
	{
		PrintUsage( argv[0] );
		return ERR_COMMAND_LINE;
	}
	PTCHAR szSourceFile = argv[1]
	return ProcessFile( szSourceFile );
}


void PrintUsage( PTCHAR szAppName )
{
	std::cerr << "Please give filename as command line.\n\n\t" << szAppName << " filename\n\t" << std::endl;
}


int ProcessFile( PTCHAR szFileName )
{
	HANDLE hFile = CreateFile( szFileName, GENERIC_READ, FILE_SHARE_WRITE,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if( hFile == NULL || hFile == INVALID_HANDLE_VALUE )
	{
		return ERR_FILE_ACCESS;
	}

	DWORD dwFileLength = GetFileSize( hFile, NULL );
	PBYTE pbyFileContents = new BYTE[ dwFileLength ];
	if( !pbyFileContents )
	{
		return ERR_FILE_MEMORY;
	}
	DWORD dwRead = 0;
	ReadFile( hFile, pbyFileContents, dwFileLength, &dwRead, NULL );
	if( dwFileLength != dwRead )
	{
		return ERR_FILE_OPER;
	}
	CloseHandle( hFile );


	PBYTE pbyOutputBuffer = new BYTE[ dwFileLength ];
	DWORD dwOpLength;
	// :TODO: No special handling of Unicode/ANSI files, UTF -8 will just work with ANSI version
	RemoveComments(( PTCHAR )pbyFileContents, dwFileLength, ( PTCHAR )pbyOutputBuffer, &dwOpLength );
	delete[] pbyFileContents;

	hFile = CreateFile( szFileName, GENERIC_WRITE, FILE_SHARE_WRITE,
		NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
	if( hFile == NULL || hFile == INVALID_HANDLE_VALUE )
	{
		return ERR_FILE_ACCESS;
	}
	DWORD dwWritten = 0;
	WriteFile( hFile, pbyOutputBuffer, dwOpLength, &dwWritten, NULL );
	if( dwOpLength != dwOpLength )
	{
		return ERR_FILE_OPER;
	}
	CloseHandle( hFile );
	delete[] pbyOutputBuffer;
	return 0;
}


void RemoveComments( PTCHAR szInput, DWORD dwInpCount, PTCHAR szOutput, PDWORD dwOpCount )
{
	DWORD dwCharPointer = 0;
	*dwOpCount = 0;
	LPCTSTR pszCommentEnd = NULL;
	int nCmtEndSize = 0;
	bool bQuote = false;
	while ( dwCharPointer < dwInpCount )
	{
		if ( !pszCommentEnd && szInput[dwCharPointer] == QUOTE && dwCharPointer )
		{
			if(( szInput[dwCharPointer - 1] == ESCAPE_SEQ && szInput[dwCharPointer - 2] == ESCAPE_SEQ ) ||
				szInput[dwCharPointer - 1] != ESCAPE_SEQ )
			{
				bQuote = !bQuote;
			}
		}
		// Checks for start of C/CPP comment
		if( !bQuote && !pszCommentEnd )
		{
			if( !memcmp( &szInput[dwCharPointer], COMMENT_C_START, sizeof( COMMENT_C_START ) - 1 ))
			{
				pszCommentEnd = COMMENT_C_END;
				dwCharPointer += sizeof( COMMENT_C_START ) - 1;
				nCmtEndSize = sizeof( COMMENT_C_END ) - 1;
			}
			else if( !memcmp( &szInput[dwCharPointer], COMMENT_CPP_START, sizeof( COMMENT_CPP_START ) - 1))
			{
				pszCommentEnd = COMMENT_CPP_END;
				dwCharPointer += sizeof( COMMENT_C_START ) - 1;
				nCmtEndSize = sizeof( COMMENT_CPP_END ) - 1;
			}
			if ( pszCommentEnd )
			{
				continue;
			}
		}
		// Writes every character if currently not processing a comment
		if( !pszCommentEnd )
		{
			WriteBuffer( szOutput, szInput[dwCharPointer], dwOpCount, false );
		}
		else
		{
			// Checks the end of comment if currently processing a comment
			if( !memcmp( &szInput[dwCharPointer], pszCommentEnd, nCmtEndSize ))
			{
				// CPP comments need to add CR/LF so don't increment
				if ( pszCommentEnd == COMMENT_C_END )
				{
					dwCharPointer += nCmtEndSize;
				}
				else if ( szInput[dwCharPointer - 1] == CARRIAGE_RETURN )
				{
					dwCharPointer--;
				}
				pszCommentEnd = NULL;
				g_bCommentLine = true;
				continue;
			}
		}
		dwCharPointer++;
	}
	WriteBuffer( szOutput, 0, dwOpCount, true );
}


void WriteBuffer( PTCHAR szDataString, TCHAR ch, PDWORD dwOpCount, bool flush )
{
	const int USUAL_MAX_LINE_SIZE = 150;
	static PTCHAR szLineBuffer = NULL;
	static DWORD dwCount = 0;
	static DWORD dwCurrentAlloc = 0;
	if ( dwCount >= dwCurrentAlloc )
	{
		dwCurrentAlloc += USUAL_MAX_LINE_SIZE;
		szLineBuffer = ( PTCHAR )( dwCount ? realloc( szLineBuffer, dwCurrentAlloc ) : malloc( dwCurrentAlloc ));
	}
	if ( !flush )
	{
		szLineBuffer[dwCount++] = ch;
	}
	if ( ch == LINE_FEED || flush )
	{
		memcpy( &szDataString[*dwOpCount], szLineBuffer, dwCount );
		if ( !g_bCommentLine )
		{
			memcpy( &szDataString[*dwOpCount], szLineBuffer, dwCount );
		}
		else if( HasImportantText( szLineBuffer, dwCount ))
		{
			memcpy( &szDataString[*dwOpCount], szLineBuffer, dwCount );
			g_bCommentLine = false;
		}
		else
		{
			g_bCommentLine = false;
			dwCount = 0;
		}
		*dwOpCount += dwCount;
		dwCount = 0;
	}
	if ( flush )
	{
		dwCurrentAlloc = 0;
		free( szLineBuffer );
	}
}

bool HasImportantText( PTCHAR szBuffer, DWORD dwBufLength )
{
	DWORD dwCount = 0;
	while( dwBufLength > dwCount )
	{
		TCHAR ch = szBuffer[dwCount++];
		if( !_tcschr( UNIMPORTANT_TEXT, ch ))
		{
			return true;
		}
	}
	return false;
}